#include "config.h"

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#include "inspector.h"
#include "redismodule.h"

static char *trim(char *str) {
    if (!str) return NULL;

    // 去除首部空白
    char *start = str;
    while (*start && isspace(*start)) start++;

    // 全是空白的情况
    if (*start == '\0') {
        *str = '\0';
        return str;
    }

    // 去除尾部空白
    char *end = str + strlen(str) - 1;
    while (end > start && isspace(*end)) end--;
    *(end + 1) = '\0';

    // 如果有首部空白，需要移动字符串
    if (start != str) {
        memmove(str, start, end - start + 2);
    }

    return str;
}

/**
 * 读取指定路径的配置文件
 * 只支持绝对路径
 */
int load_config(RedisModuleCtx *ctx, const char *path, TaskConfig *config) {
    FILE *file = fopen(path, "r");
    if (file == NULL) {
        RedisModule_Log(ctx, "warning", "Failed to open config file: %s", path);
        return -1;
    }

    // 获取文件大小
    struct stat st;
    if (stat(path, &st) != 0) {
        RedisModule_Log(ctx, "warning", "Failed to get file size: %s", path);
        fclose(file);
        return -1;
    }

    // 分配内存并读取文件内容
    char *content = RedisModule_Alloc(st.st_size + 1);
    size_t read_size = fread(content, 1, st.st_size, file);
    content[read_size] = '\0';
    fclose(file);

    // 解析配置文件内容
    char *file_ptr = NULL;
    char *line = strtok_r(content, "\n", &file_ptr);
    while (line != NULL) {
        // 去除行首尾的空白字符
        line = trim(line);

        // 忽略空行和注释行
        if (line[0] == '\0' || line[0] == '#') {
            line = strtok_r(NULL, "\n", &file_ptr);
            continue;
        }

        // 解析配置项
        char *line_ptr = NULL;
        char *key = strtok_r(line, "=", &line_ptr);
        char *value = strtok_r(NULL, "=", &line_ptr);
        if (key == NULL || value == NULL) {
            line = strtok_r(NULL, "\n", &file_ptr);
            continue;
        }

        // 去除键值对首尾的空白字符
        key = trim(key);
        value = trim(value);
        
        int is_valid = 1;
        if (strcmp(key, "enable") == 0) {
            config->enable = atoi(value);
        } else if (strcmp(key, "exclusive") == 0) {
            config->exclusive = atoi(value);
        } else if (strcmp(key, "interval") == 0) {
            config->interval = atoi(value);
        } else if (strcmp(key, "scan_tick") == 0) {
            config->scan_tick = atoi(value);
        } else if (strcmp(key, "scan_count") == 0) {
            config->scan_count = atoi(value);
        } else if (strcmp(key, "items_threshold") == 0) {
            config->items_threshold = atoi(value);
        } else if (strcmp(key, "memory_threshold") == 0) {
            config->memory_threshold = atoll(value);
        } else if (strcmp(key, "ttl_threshold") == 0) {
            config->ttl_threshold = atoi(value);
        } else {
            is_valid = 0;
        }

        if (is_valid) {
            RedisModule_Log(ctx, "notice", "load inspector config [%s] to [%s]", key, value);    
        } else {
            RedisModule_Log(ctx, "warning", "unknown inspector config [%s]", key);
        }

        line = strtok_r(NULL, "\n", &file_ptr);
    }

    RedisModule_Free(content);
    RedisModule_Log(ctx, "notice", "Success load config file: %s", path);
    return 0;
}

/**
 * 将配置写入文件
 */
int flush_config(RedisModuleCtx *ctx, const char *path, const TaskConfig *config) {
    FILE *file = fopen(path, "w");
    if (file == NULL) {
        RedisModule_Log(ctx, "warning", "Failed to open config file: %s", path);
        return -1;
    }

    fprintf(file, "enable=%d\n", config->enable);
    fprintf(file, "exclusive=%d\n", config->exclusive);
    fprintf(file, "interval=%u\n", config->interval);
    fprintf(file, "scan_tick=%u\n", config->scan_tick);
    fprintf(file, "scan_count=%u\n", config->scan_count);
    fprintf(file, "items_threshold=%u\n", config->items_threshold);
    fprintf(file, "memory_threshold=%lu\n", config->memory_threshold);
    fprintf(file, "ttl_threshold=%u\n", config->ttl_threshold);

    fclose(file);
    RedisModule_Log(ctx, "notice", "Success flush config to path: %s", path);
    return 0;
}
